﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Generic;
using Pfz.Threading;
using Pfz.Extensions;
using Pfz.DynamicObjects.Internal;
using System.Reflection.Emit;
using Pfz.DataTypes;

namespace Pfz
{
	/// <summary>
	/// This class allows you to get members from types more safely than using
	/// string literals. It only exists because C# does not have fieldinfoof,
	/// propertyinfoof and methodinfoof.
	/// </summary>
	public static class ReflectionHelper
	{
		#region GetMember
			/// <summary>
			/// Gets a member by it's expression usage.
			/// For example, GetMember(() => obj.GetType()) will return the
			/// GetType method.
			/// </summary>
			public static MemberInfo GetMember<T>(Expression<Func<T>> expression)
			{
				if (expression == null)
					throw new ArgumentNullException("expression");
				
				var body = expression.Body;
			
				switch(body.NodeType)
				{
					case ExpressionType.MemberAccess:
						MemberExpression memberExpression = (MemberExpression)body;
						return memberExpression.Member;
				
					case ExpressionType.Call:
						MethodCallExpression callExpression = (MethodCallExpression)body;
						return callExpression.Method;
				
					case ExpressionType.New:
						NewExpression newExpression = (NewExpression)body;
						return newExpression.Constructor;
				}
			
				throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
			}
		#endregion
		#region GetConstructor
			/// <summary>
			/// Gets the constructor info from a sample construction call expression.
			/// Example: GetConstructor(() => new Control()) will return the constructor
			/// info for the default constructor of Control.
			/// </summary>
			public static ConstructorInfo GetConstructor<T>(Expression<Func<T>> expression)
			{
				return (ConstructorInfo)GetMember(expression);
			}
		#endregion
		#region GetField
			/// <summary>
			/// Gets a field from a sample usage.
			/// Example: GetField(() => Type.EmptyTypes) will return the FieldInfo of
			/// EmptyTypes.
			/// </summary>
			public static FieldInfo GetField<T>(Expression<Func<T>> expression)
			{
				return (FieldInfo)GetMember(expression);
			}
		#endregion
		#region GetProperty
			/// <summary>
			/// Gets a property from a sample usage.
			/// Example: GetProperty(() => str.Length) will return the property info 
			/// of Length.
			/// </summary>
			public static PropertyInfo GetProperty<T>(Expression<Func<T>> expression)
			{
				return (PropertyInfo)GetMember(expression);
			}
		#endregion
		#region GetMethod
			/// <summary>
			/// Gets a method info of a void method.
			/// Example: GetMethod(() => Console.WriteLine("")); will return the
			/// MethodInfo of WriteLine that receives a single argument.
			/// </summary>
			public static MethodInfo GetMethod(Expression<Action> expression)
			{
				if (expression == null)
					throw new ArgumentNullException("expression");
			
				var body = expression.Body;
				if (body.NodeType != ExpressionType.Call)
					throw new ArgumentException("expression.Body must be a Call expression.", "expression");
			
				MethodCallExpression callExpression = (MethodCallExpression)body;
				return callExpression.Method;
			}
		
			/// <summary>
			/// Gets the MethodInfo of a method that returns a value.
			/// Example: GetMethod(() => Console.ReadLine()); will return the method info
			/// of ReadLine.
			/// </summary>
			public static MethodInfo GetMethod<T>(Expression<Func<T>> expression)
			{
				return (MethodInfo)GetMember(expression);
			}
		#endregion

		#region GetDefaultConstructorDelegate
			private static YieldReaderWriterLockSlim _defaultConstructorsLock;
			private static readonly Dictionary<Type, Func<object>> _defaultConstructorsDictionary = new Dictionary<Type, Func<object>>();
			private static readonly Func<Type, Func<object>> _getDefaultConstructorDelegate = GetDefaultConstructorDelegate<object>;

			/// <summary>
			/// Gets a function that creates objects of the given type.
			/// The object must have a default constructor.
			/// </summary>
			public static Func<object> GetDefaultConstructorDelegate(Type objectType)
			{
				var result = _defaultConstructorsDictionary.GetOrCreateValue(ref _defaultConstructorsLock, objectType, _getDefaultConstructorDelegate);
				return result;
			}

			private static YieldReaderWriterLockSlim _typedDefaultConstructorsLock;
			private static readonly Dictionary<KeyValuePair<Type, Type>, Delegate> _typedDefaultConstructorsDictionary = new Dictionary<KeyValuePair<Type, Type>, Delegate>();
			private static readonly Func<KeyValuePair<Type, Type>, Delegate> _getTypedDefaultConstructorDelegate = _GetDefaultConstructorDelegate;
			/// <summary>
			/// Gets the default constructor for the given objectType, but return
			/// it already casted to a given "T".
			/// </summary>
			public static Func<T> GetDefaultConstructorDelegate<T>(Type objectType)
			{
				if (objectType == null)
					throw new ArgumentNullException("objectType");
					
				var pair = Pair.Create(typeof(Func<T>), objectType);
				var result = _typedDefaultConstructorsDictionary.GetOrCreateValue(ref _typedDefaultConstructorsLock, pair, _getTypedDefaultConstructorDelegate);
				return (Func<T>)result;
			}
			
			private static Delegate _GetDefaultConstructorDelegate(KeyValuePair<Type, Type> pair)
			{
				var funcType = pair.Key;
				var objectType = pair.Value;
				var resultType = funcType.GetGenericArguments()[0];

				Expression expression = Expression.New(objectType);
			
				if 
				(
					resultType != objectType &&
					(
						objectType.IsValueType ||
						!resultType.IsAssignableFrom(objectType)
					)
				)
				{
					expression = Expression.Convert(expression, resultType);
				}
				
				var lambdaExpression = Expression.Lambda(funcType, expression);
				var func = lambdaExpression.Compile();

				return func;
			}
		#endregion
		#region GetConstructorDelegate
			private static YieldReaderWriterLockSlim _constructorsLock;
			private static readonly Dictionary<ConstructorInfo, FastDynamicDelegate> _constructors = new Dictionary<ConstructorInfo, FastDynamicDelegate>();
			private static readonly Func<ConstructorInfo, FastDynamicDelegate> _getConstructorDelegate = _GetConstructorDelegate;
			public static FastDynamicDelegate GetConstructorDelegate(ConstructorInfo constructor)
			{
				if (constructor == null)
					throw new ArgumentNullException("constructor");
			
				var result = _constructors.GetOrCreateValue(ref _constructorsLock, constructor, _getConstructorDelegate);
				return result;
			}
			
			private static readonly Type[] _typeOfObjects = new Type[]{typeof(object[])};
			private static FastDynamicDelegate _GetConstructorDelegate(ConstructorInfo constructor)
			{
				var declaringType = constructor.DeclaringType;
				var method = new DynamicMethod("Create", typeof(object), _typeOfObjects, true);
				var generator = method.GetILGenerator();
				
				int parameterIndex = -1;
				foreach(var parameter in constructor.GetParameters())
				{
					parameterIndex++;
					
					generator.Emit(OpCodes.Ldarg_0);
					generator.Emit(OpCodes.Ldc_I4, parameterIndex);
					generator.Emit(OpCodes.Ldelem, typeof(object));
					
					var parameterType = parameter.ParameterType;
					if (parameterType != typeof(object))
						generator.Emit(OpCodes.Unbox_Any, parameterType);
				}
				
				generator.Emit(OpCodes.Newobj, constructor);
				
				if (declaringType.IsValueType)
					generator.Emit(OpCodes.Box, declaringType);
				
				generator.Emit(OpCodes.Ret);
				
				var result = method.CreateDelegate(typeof(FastDynamicDelegate));
				return (FastDynamicDelegate)result;
			}
		#endregion
		#region GetConstructorDelegate<T>
			public static T GetConstructorDelegate<T>(ConstructorInfo constructor)
			{
				object result = GetConstructorDelegate(constructor, typeof(T));
				return (T)result;
			}
			
			private static YieldReaderWriterLockSlim _getTypedConstructorLock;
			private static readonly Dictionary<KeyValuePair<ConstructorInfo, Type>, Delegate> _typedConstructors = new Dictionary<KeyValuePair<ConstructorInfo, Type>, Delegate>();
			private static readonly Func<KeyValuePair<ConstructorInfo, Type>, Delegate> _getTypedConstructorDelegate = _GetConstructorDelegate;
			public static Delegate GetConstructorDelegate(ConstructorInfo constructor, Type delegateType)
			{
				if (constructor == null)
					throw new ArgumentNullException("constructor");
					
				if (!delegateType.IsSubclassOf(typeof(Delegate)))
					throw new ArgumentException("delegateType is not a Delegate.", "delegateType");
					
				var pair = Pair.Create(constructor, delegateType);
				var result = _typedConstructors.GetOrCreateValue(ref _getTypedConstructorLock, pair, _getTypedConstructorDelegate);
				return result;
			}
					
			private static Delegate _GetConstructorDelegate(KeyValuePair<ConstructorInfo, Type> pair)
			{
				var constructor = pair.Key;
				var delegateType = pair.Value;
				
				var invokeMethod = delegateType.GetMethod("Invoke");
				if (invokeMethod == null)
					throw new InvalidOperationException("The given delegate type does not have an Invoke method. Is this a compilation error?");
					
				var constructorType = constructor.DeclaringType;
				var invokeReturnType = invokeMethod.ReturnType;
				
				bool isInvokeVoid = invokeReturnType == typeof(void);
				if (isInvokeVoid)
					throw new InvalidOperationException("The return type the delegate is incompatible.");
					
				var invokeParameterTypes = invokeMethod.GetParameterTypes();
				var constructorParameterTypes = constructor.GetParameterTypes();
					
				int count = invokeParameterTypes.Length;
				if (constructorParameterTypes.Length != count)
					throw new InvalidOperationException("The number of parameters between the constructor and the delegate is not compatible.");
				
				var parameterExpressions = new ParameterExpression[count];
				var arguments = new Expression[count];
				for(int i=0; i<count; i++)
				{
					var argument = _GetArgumentExpression(i, constructorParameterTypes, invokeParameterTypes, parameterExpressions);
					arguments[i] = argument;
				}
					
				Expression resultExpression = Expression.New(constructor, arguments);

				if (constructorType != invokeReturnType)
					resultExpression = Expression.Convert(resultExpression, invokeReturnType);
					
				var lambda = Expression.Lambda(delegateType, resultExpression, parameterExpressions);
				var compiled = lambda.Compile();
				return compiled;
			}
		#endregion

		#region GetPropertyGetterDelegate
			private static YieldReaderWriterLockSlim _getPropertyLock;
			private static readonly Dictionary<PropertyInfo, Func<object, object>> _getPropertiesDictionary = new Dictionary<PropertyInfo,Func<object, object>>();
			private static readonly Func<PropertyInfo, Func<object, object>> _getPropertyGetterDelegate = GetPropertyGetterDelegate<object, object>;
			/// <summary>
			/// Gets a delegate to read values from the given property in a very fast manner.
			/// </summary>
			public static Func<object, object> GetPropertyGetterDelegate(PropertyInfo property)
			{
				var result = _getPropertiesDictionary.GetOrCreateValue(ref _getPropertyLock, property, _getPropertyGetterDelegate); 
				return result;
			}

			private static YieldReaderWriterLockSlim _getTypedPropertyLock;
			private static readonly Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate> _getTypedPropertiesDictionary = new Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate>();
			private static readonly Func<KeyValuePair<Type, PropertyInfo>, Delegate> _getTypedPropertyGetterDelegate = _GetPropertyGetterDelegate;
			/// <summary>
			/// Gets a delegate to read values from the given property in a very fast manner.
			/// The result will be already cast or will even avoid casts if the 
			/// generic types are correct.
			/// </summary>
			public static Func<TInstance, TOutput> GetPropertyGetterDelegate<TInstance, TOutput>(PropertyInfo property)
			{
				if (property == null)
					throw new ArgumentNullException("property");
					
				var pair = Pair.Create(typeof(Func<TInstance, TOutput>), property);
				var result = _getTypedPropertiesDictionary.GetOrCreateValue(ref _getTypedPropertyLock, pair, _getTypedPropertyGetterDelegate);
				return (Func<TInstance, TOutput>)result;
			}
			
			private static Delegate _GetPropertyGetterDelegate(KeyValuePair<Type, PropertyInfo> pair)
			{
				var funcType = pair.Key;
				var property = pair.Value;
				
				var funcArguments = funcType.GetGenericArguments();
				var instanceType = funcArguments[0];
				var resultType = funcArguments[1];
				
				var parameter = Expression.Parameter(instanceType, "instance");
				Expression resultExpression;

				var getMethod = property.GetGetMethod();
				if (getMethod == null)
					throw new ArgumentException("Property " + property.Name +  " can't be read.", "read");

				if (getMethod.IsStatic)
					resultExpression = Expression.MakeMemberAccess(null, property);
				else
				{
					Expression readParameter = parameter;
			
					if (property.DeclaringType != instanceType)
						readParameter = Expression.Convert(parameter, property.DeclaringType);
			
					resultExpression = Expression.MakeMemberAccess(readParameter, property);
				}

				if (property.PropertyType != resultType)
					resultExpression = Expression.Convert(resultExpression, resultType);
				
				var lambda = Expression.Lambda(funcType, resultExpression, parameter);

				var result = lambda.Compile();
				return result;
			}
		#endregion
		#region GetPropertySetterDelegate
			private static YieldReaderWriterLockSlim _setPropertyLock;
			private static readonly Dictionary<PropertyInfo, Action<object, object>> _setPropertiesDictionary = new Dictionary<PropertyInfo, Action<object, object>>();
			private static readonly Func<PropertyInfo, Action<object, object>> _getPropertySetterDelegate = GetPropertySetterDelegate<object, object>;
			/// <summary>
			/// Gets a delegate that can be used to do very fast sets on the given property.
			/// </summary>
			public static Action<object, object> GetPropertySetterDelegate(PropertyInfo property)
			{
				var result = _setPropertiesDictionary.GetOrCreateValue(ref _setPropertyLock, property, _getPropertySetterDelegate);
				return result;
			}

			private static YieldReaderWriterLockSlim _typedPropertySetterDelegatesLock;
			private static readonly Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate> _typedPropertySetterDelegatesDictionary = new Dictionary<KeyValuePair<Type, PropertyInfo>, Delegate>();
			private static Func<KeyValuePair<Type, PropertyInfo>, Delegate> _typedGetPropertySetterDelegate = _TypedGetPropertySetterDelegate;
			/// <summary>
			/// Gets a delegate that can be used to do very fast sets on the given property.
			/// If the generic types are correct, casts can be avoided to improve performance
			/// even further.
			/// </summary>
			public static Action<TInstance, TValue> GetPropertySetterDelegate<TInstance, TValue>(PropertyInfo property)
			{
				if (property == null)
					throw new ArgumentNullException("property");
					
				var pair = Pair.Create(typeof(Action<TInstance, TValue>), property);
				var result = _typedPropertySetterDelegatesDictionary.GetOrCreateValue(ref _typedPropertySetterDelegatesLock, pair, _typedGetPropertySetterDelegate);
				return (Action<TInstance, TValue>)result;
			}
			
			private static Delegate _TypedGetPropertySetterDelegate(KeyValuePair<Type, PropertyInfo> pair)
			{
				var actionType = pair.Key;
				var property = pair.Value;
				var actionArguments = actionType.GetGenericArguments();
				var instanceType = actionArguments[0];
				var valueType = actionArguments[1];
				
				var instanceParameter = Expression.Parameter(instanceType, "instance");

				var valueParameter = Expression.Parameter(valueType, "value");
				Expression readValueParameter = valueParameter;
				if (property.PropertyType != valueType)
					readValueParameter = Expression.Convert(valueParameter, property.PropertyType);

				// .Net 3.5 does not have assign
				// but we can call the set method directly (and we need it to test for static).
				var setMethod = property.GetSetMethod(true);
				if (setMethod == null)
					throw new ArgumentException("Property " + property.Name + " is read-only.", "property");

				Expression setExpression;
				if (setMethod.IsStatic)
					setExpression = Expression.Call(setMethod, readValueParameter);
				else
				{
					Expression readInstanceParameter = instanceParameter;
					if (property.DeclaringType != instanceType)
						readInstanceParameter = Expression.Convert(instanceParameter, property.DeclaringType);
				
					setExpression = Expression.Call(readInstanceParameter, setMethod, readValueParameter);
				}

				var lambda = Expression.Lambda(actionType, setExpression, instanceParameter, valueParameter);
				var result = lambda.Compile();
				return result;
			}
		#endregion
		#region GetEventAdder
			private static YieldReaderWriterLockSlim _addEventLock;
			private static readonly Dictionary<EventInfo, Action<object, Delegate>> _addEventDictionary = new Dictionary<EventInfo, Action<object, Delegate>>();
			private static readonly Func<EventInfo, Action<object, Delegate>> _getEventAdderDelegate = GetEventAdderDelegate<object, Delegate>;

			/// <summary>
			/// Gets a delegate to do fast "event add"
			/// </summary>
			public static Action<object, Delegate> GetEventAdderDelegate(EventInfo eventInfo)
			{
				var result = _addEventDictionary.GetOrCreateValue(ref _addEventLock, eventInfo, _getEventAdderDelegate);
				return result;
			}

			/// <summary>
			/// Gets a delegate to do fast "event add".
			/// Can avoid casts if the right generic types are given.
			/// </summary>
			public static Action<TInstance, TDelegate> GetEventAdderDelegate<TInstance, TDelegate>(EventInfo eventInfo)
			{
				return _GetEventDelegate<TInstance, TDelegate>(eventInfo.GetAddMethod(), eventInfo.EventHandlerType);
			}
		#endregion
		#region GetEventRemoverDelegate
			private static YieldReaderWriterLockSlim _removeEventLock;
			private static readonly Dictionary<EventInfo, Action<object, Delegate>> _removeEventDictionary = new Dictionary<EventInfo, Action<object, Delegate>>();
			private static readonly Func<EventInfo, Action<object, Delegate>> _getEventRemoverDelegate = GetEventRemoverDelegate<object, Delegate>;

			/// <summary>
			/// Gets a delegate to do fast "event remove" calls.
			/// </summary>
			public static Action<object, Delegate> GetEventRemoverDelegate(EventInfo eventInfo)
			{
				var result = _removeEventDictionary.GetOrCreateValue(ref _removeEventLock, eventInfo, _getEventRemoverDelegate);
				return result;
			}
			/// <summary>
			/// Gets a delegate to do fast "event remove" calls.
			/// Can avoid casts if the right generic types are given.
			/// </summary>
			public static Action<TInstance, TDelegate> GetEventRemoverDelegate<TInstance, TDelegate>(EventInfo eventInfo)
			{
				return _GetEventDelegate<TInstance, TDelegate>(eventInfo.GetRemoveMethod(), eventInfo.EventHandlerType);
			}
		#endregion
		#region _GetEventDelegate
			private static Action<TInstance, TDelegate> _GetEventDelegate<TInstance, TDelegate>(MethodInfo method, Type handlerType)
			{
				var instanceParameter = Expression.Parameter(typeof(TInstance), "instance");
				var handlerParameter = Expression.Parameter(typeof(TDelegate), "handler");
				Expression readHandlerParameter = handlerParameter;
				if (handlerType != typeof(TDelegate))
					readHandlerParameter = Expression.Convert(handlerParameter, handlerType);

				Expression callExpression;
				if (method.IsStatic)
					callExpression = Expression.Call(method, readHandlerParameter);
				else
				{
					Expression readInstanceParameter = instanceParameter;
					if (method.DeclaringType != typeof(TInstance))
						readInstanceParameter = Expression.Convert(instanceParameter, method.DeclaringType);
				
					callExpression = Expression.Call(readInstanceParameter, method, readHandlerParameter);
				}

				var lambda = Expression.Lambda<Action<TInstance, TDelegate>>(callExpression, instanceParameter, handlerParameter);
				var result = lambda.Compile();
				return result;
			}
		#endregion

		#region GetFieldGetterDelegate
			private static YieldReaderWriterLockSlim _getFieldLock;
			private static readonly Dictionary<FieldInfo, Func<object, object>> _getFieldsDictionary = new Dictionary<FieldInfo,Func<object, object>>();
			private static readonly Func<FieldInfo, Func<object, object>> _getFieldGetterDelegate = GetFieldGetterDelegate<object, object>;
			/// <summary>
			/// Gets a delegate to read values from the given field in a very fast manner.
			/// </summary>
			public static Func<object, object> GetFieldGetterDelegate(FieldInfo field)
			{
				var result = _getFieldsDictionary.GetOrCreateValue(ref _getFieldLock, field, _getFieldGetterDelegate); 
				return result;
			}

			private static YieldReaderWriterLockSlim _getTypedFieldLock;
			private static readonly Dictionary<KeyValuePair<Type, FieldInfo>, Delegate> _getTypedFieldsDictionary = new Dictionary<KeyValuePair<Type, FieldInfo>, Delegate>();
			private static readonly Func<KeyValuePair<Type, FieldInfo>, Delegate> _getTypedFieldGetterDelegate = _GetFieldGetterDelegate;
			/// <summary>
			/// Gets a delegate to read values from the given field in a very fast manner.
			/// The result will be already cast or will even avoid casts if the 
			/// generic types are correct.
			/// </summary>
			public static Func<TInstance, TOutput> GetFieldGetterDelegate<TInstance, TOutput>(FieldInfo field)
			{
				if (field == null)
					throw new ArgumentNullException("field");
					
				var pair = Pair.Create(typeof(Func<TInstance, TOutput>), field);
				var result = _getTypedFieldsDictionary.GetOrCreateValue(ref _getTypedFieldLock, pair, _getTypedFieldGetterDelegate);
				return (Func<TInstance, TOutput>)result;
			}
			private static Delegate _GetFieldGetterDelegate(KeyValuePair<Type, FieldInfo> pair)
			{
				var funcType = pair.Key;
				var field = pair.Value;
				
				var funcArguments = funcType.GetGenericArguments();
				var instanceType = funcArguments[0];
				var resultType = funcArguments[1];
				
				var parameter = Expression.Parameter(instanceType, "instance");
				Expression resultExpression;

				if (field.IsStatic)
					resultExpression = Expression.MakeMemberAccess(null, field);
				else
				{
					Expression readParameter = parameter;
			
					if (field.DeclaringType != instanceType)
						readParameter = Expression.Convert(parameter, field.DeclaringType);
			
					resultExpression = Expression.MakeMemberAccess(readParameter, field);
				}

				if (field.FieldType != resultType)
					resultExpression = Expression.Convert(resultExpression, resultType);
				
				var lambda = Expression.Lambda(funcType, resultExpression, parameter);

				var result = lambda.Compile();
				return result;
			}
		#endregion

		#region GetMethodCallDelegate<T>
			public static T GetMethodCallDelegate<T>(MethodInfo method)
			{
				object result = GetMethodCallDelegate(method, typeof(T));
				return (T)result;
			}
			
			private static YieldReaderWriterLockSlim _getTypedMethodCallLock;
			private static readonly Dictionary<KeyValuePair<MethodInfo, Type>, Delegate> _getTypedMethodCallDictionary = new Dictionary<KeyValuePair<MethodInfo, Type>, Delegate>();
			private static readonly Func<KeyValuePair<MethodInfo, Type>, Delegate> _getTypedMethodCallDelegate = _GetTypedMethodCallDelegate;
			public static Delegate GetMethodCallDelegate(MethodInfo method, Type delegateType)
			{
				if (method == null)
					throw new ArgumentNullException("method");
					
				if (!delegateType.IsSubclassOf(typeof(Delegate)))
					throw new ArgumentException("delegateType is not a Delegate.", "delegateType");
				
				var pair = Pair.Create(method, delegateType);	
				var result = _getTypedMethodCallDictionary.GetOrCreateValue(ref _getTypedMethodCallLock, pair, _getTypedMethodCallDelegate);
				return result;
			}
			private static Delegate _GetTypedMethodCallDelegate(KeyValuePair<MethodInfo, Type> pair)
			{
				var method = pair.Key;
				var delegateType = pair.Value;
				
				var invokeMethod = delegateType.GetMethod("Invoke");
				if (invokeMethod == null)
					throw new InvalidOperationException("The given delegate type does not have an Invoke method. Is this a compilation error?");
					
				var methodReturnType = method.ReturnType;
				var invokeReturnType = invokeMethod.ReturnType;
				
				bool isMethodVoid = methodReturnType == typeof(void);
				bool isInvokeVoid = invokeReturnType == typeof(void);
				if (isMethodVoid != isInvokeVoid)
					throw new InvalidOperationException("The return type of the method and the delegate is incompatible.");
					
				var invokeParameterTypes = invokeMethod.GetParameterTypes();
				var methodParameterTypes = new List<Type>();
				if (!method.IsStatic)
					methodParameterTypes.Add(method.DeclaringType);
					
				methodParameterTypes.AddRange(method.GetParameterTypes());
				
				int count = invokeParameterTypes.Length;
				if (methodParameterTypes.Count != count)
					throw new InvalidOperationException("The number of parameters between the method and the delegate is not compatible. Note that non-static methods have the additional \"this\" parameter as the first one.");
				
				var parameterExpressions = new ParameterExpression[count];

				int startIndex = 0;	
				int argumentCount = count;
				if (!method.IsStatic)
				{
					startIndex = 1;
					argumentCount--;
				}
				
				var arguments = new Expression[argumentCount];
				for(int i=0; i<argumentCount; i++)
				{
					var argument = _GetArgumentExpression(i+startIndex, methodParameterTypes, invokeParameterTypes, parameterExpressions);
					arguments[i] = argument;
				}
					
				MethodCallExpression callExpression;
				if (method.IsStatic)
					callExpression = Expression.Call(method, arguments);
				else
				{
					var instanceExpression = _GetArgumentExpression(0, methodParameterTypes, invokeParameterTypes, parameterExpressions);
					callExpression = Expression.Call(instanceExpression, method, arguments);
				}
				
				Expression resultExpression = callExpression;
				if (methodReturnType != invokeReturnType)
					resultExpression = Expression.Convert(resultExpression, invokeReturnType);
					
				var lambda = Expression.Lambda(delegateType, resultExpression, parameterExpressions);
				var compiled = lambda.Compile();
				return compiled;
			}
			private static Expression _GetArgumentExpression(int index, IList<Type> methodParameterTypes, Type[] invokeParameterTypes, ParameterExpression[] parameterExpressions)
			{
				var invokeParameterType = invokeParameterTypes[index];
				var methodParameterType = methodParameterTypes[index];
				
				var parameter = Expression.Parameter(invokeParameterType, "P" + index);
				parameterExpressions[index] = parameter;
				if (methodParameterType == invokeParameterType)
					return parameter;
					
				var convert = Expression.Convert(parameter, methodParameterType);
				return convert;
			}
		#endregion
	}

	/// <summary>
	/// This is a typed version of reflection helper, so your expression already starts with a know
	/// object type (used when you don't have an already instantiated object).
	/// </summary>
	public static class ReflectionHelper<ForType>
	{
		#region GetMember
			/// <summary>
			/// Gets a member by it's expression usage.
			/// For example, GetMember((obj) => obj.GetType()) will return the
			/// GetType method.
			/// </summary>
			public static MemberInfo GetMember<T>(Expression<Func<ForType, T>> expression)
			{
				if (expression == null)
					throw new ArgumentNullException("expression");
				
				var body = expression.Body;
			
				switch(body.NodeType)
				{
					case ExpressionType.MemberAccess:
						MemberExpression memberExpression = (MemberExpression)body;
						return memberExpression.Member;
				
					case ExpressionType.Call:
						MethodCallExpression callExpression = (MethodCallExpression)body;
						return callExpression.Method;
				
					case ExpressionType.New:
						NewExpression newExpression = (NewExpression)body;
						return newExpression.Constructor;
				}
			
				throw new ArgumentException("expression.Body must be a member or call expression.", "expression");
			}
		#endregion
		#region GetField
			/// <summary>
			/// Gets a field from a sample usage.
			/// Example: GetField((obj) => obj.SomeField) will return the FieldInfo of
			/// EmptyTypes.
			/// </summary>
			public static FieldInfo GetField<T>(Expression<Func<ForType, T>> expression)
			{
				return (FieldInfo)GetMember(expression);
			}
		#endregion
		#region GetProperty
			/// <summary>
			/// Gets a property from a sample usage.
			/// Example: GetProperty((str) => str.Length) will return the property info 
			/// of Length.
			/// </summary>
			public static PropertyInfo GetProperty<T>(Expression<Func<ForType, T>> expression)
			{
				return (PropertyInfo)GetMember(expression);
			}
		#endregion
		#region GetMethod
			/// <summary>
			/// Gets a method info of a void method.
			/// Example: GetMethod((obj) => obj.SomeCall("")); will return the
			/// MethodInfo of SomeCall that receives a single argument.
			/// </summary>
			public static MethodInfo GetMethod(Expression<Action<ForType>> expression)
			{
				if (expression == null)
					throw new ArgumentNullException("expression");
			
				var body = expression.Body;
				if (body.NodeType != ExpressionType.Call)
					throw new ArgumentException("expression.Body must be a Call expression.", "expression");
			
				MethodCallExpression callExpression = (MethodCallExpression)body;
				return callExpression.Method;
			}
		
			/// <summary>
			/// Gets the MethodInfo of a method that returns a value.
			/// Example: GetMethod((obj) => obj.SomeCall()); will return the method info
			/// of SomeCall.
			/// </summary>
			public static MethodInfo GetMethod<T>(Expression<Func<ForType, T>> expression)
			{
				return (MethodInfo)GetMember(expression);
			}
		#endregion
	}
}
